#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

namespace mynet
{
    const uint16_t DEFAULT_PORT = 6666;
    class UdpServer
    {
        enum
        {
            SOCKET_ERR = 1,
            BIND_ERR
        };

    public:
        UdpServer(uint16_t port = DEFAULT_PORT)
            : _port(port)
        {
            std::cout << "echo服务器端口号: " << _port << std::endl;
        }

        void init_server()
        {
            // 1.创建socket，打开网络文件
            _sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sock_fd < 0)
            {
                std::cerr << "创建socket文件描述符失败: " << strerror(errno) << std::endl;
                exit(SOCKET_ERR);
            }
            std::cout << "创建socket文件描述符成功: " << _sock_fd << std::endl; // 3

            // 2.绑定ip和端口
            struct sockaddr_in local;
            bzero(&local, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port); // 需要转为网络字节序
            // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 将字符串点分十进制转为4字节整数，且转为网络字节序
            local.sin_addr.s_addr = INADDR_ANY; // 云服务器, 绑定到本服务器的任意ip地址

            if (bind(_sock_fd, (struct sockaddr *)&local, sizeof(local)) == -1)
            {
                std::cerr << "绑定失败: " << strerror(errno) << std::endl;
                exit(BIND_ERR);
            }
            std::cout << "绑定socket成功: " << _sock_fd << std::endl;
        }

        void start_server()
        {
            char buf[1024]; // 接收来自客户端发送的字符串
            while (true)
            {
                // 收数据: 从_sock_fd文件中拿数据放入buf
                // 服务端需要知道是哪个客户端发的数据，也就是
                // 需要知道客户端的套接字，即[client_ip, client_port]
                struct sockaddr_in client;
                // socklen_t len = 0; ???
                socklen_t len = sizeof(client);
                int n = recvfrom(_sock_fd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&client, &len);
                if(n > 0)
                    buf[n] = '\0';
                else
                    continue;

                // 打印客户端信息
                std::string client_ip = inet_ntoa(client.sin_addr);
                uint16_t client_port = ntohs(client.sin_port);
                std::cout << "[" << client_ip << "-" << client_port << "]# " << buf << std::endl;

                // 发数据: 将数据写入_sock_fd文件，发送给指定套接字的客户端
                // 这里client不需要从主机序列转到网络序列，因为client套接字本来就是从网络里读出来的，
                // 因此本来就是网络序列，直接写到_sock_fd里即可。
                sendto(_sock_fd, buf, strlen(buf), 0, (struct sockaddr *)&client, sizeof(client));
            }
        }

        ~UdpServer()
        {
        }

    private:
        int _sock_fd; // socket文件描述符
        // std::string _ip; // 点分十进制ip地址
        uint16_t _port; // 端口号
    };
}